﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using DiscreteEventModel;
using DiscreteEventModel.Common;


namespace DiscreteEventModelTest {

    public class Cacheable : Finalizable, IBinarySerializable {

        public readonly int Key;
        private int _data;

        public int Data {
            get {
                return this._data;
            }
            set {
                this._data = value;
                this.Synchronized = false;
            }
        }

        public bool Synchronized { get; private set; }

        public Cacheable(int key, Cache<int, Cacheable> cache)
            : base(cache) {
            this.Key = key;
        }

        #region IBinarySerializable Members

        public void Serialize(IBinaryWriter writer) {
            writer.Write(this._data);
            this.Synchronized = true;

            Debug.Print("serialized");
        }

        public void Deserialize(IBinaryReader reader) {
            this._data = reader.ReadInt32();
            this.Synchronized = true;

            Debug.Print("deserialized");
        }

        #endregion
    }

    public class CacheTest : ITest, Cache<int, Cacheable>.IContext {

        private Cache<int, Cacheable> _cache;
        ObjectFile<int> _backStore;
        private Random _rand;
        private int _maxCount;
        private ConcurrentDictionary<int, Cacheable> _pendingWriteItems;
        private bool _isDumping;
        private AutoResetEvent _dumpSignal;

        public CacheTest(int maxCount) {
            this._rand = new Random();
            this._cache = new Cache<int, Cacheable>(this);
            this._backStore = new ObjectFile<int>(new DirectoryInfo(@"C:\Users\Haymann\Documents\Temp"), Guid.NewGuid(), 32, 1024 * 1024);
            this._maxCount = maxCount;
            this._pendingWriteItems = new ConcurrentDictionary<int, Cacheable>();
            this._isDumping = false;
            this._dumpSignal = new AutoResetEvent(false);
            //new Thread(new ThreadStart(this.Dump)).Start();
        }

        private void Dump() {
            while(this._dumpSignal.WaitOne()) {
                while(this._pendingWriteItems.Count > 0) {
                    int key;
                    key = this._pendingWriteItems.Keys.First();
                    Cacheable item;
                    Debug.Assert(this._pendingWriteItems.TryRemove(key, out item));
                    using(IBinaryWriter writer = this._backStore.Write(key)) {
                        item.Serialize(writer);
                    }
                }
                this._isDumping = false;
            }
        }

        #region IContext Members

        public bool Read(int key, out Cacheable obj) {
            using(IBinaryReader reader = this._backStore.Read(key)) {
                obj = new Cacheable(key, this._cache);
                obj.Deserialize(reader);
            }
            return true;
        }

        public bool Write(Cacheable obj, out int key) {
            key = obj.Key;

            if(obj.Synchronized) {
                return true;
            }

            //this._pendingWriteItems[key] = obj;
            //if(!this._isDumping) {
            //    this._isDumping = true;
            //    this._dumpSignal.Set();
            //}
            //return false;

            using(IBinaryWriter writer = this._backStore.Write(key)) {
                obj.Serialize(writer);
            }
            return false;
        }

        public void CancelAsyncWrite(int key, Cacheable obj) {
            //bool b = this._pendingWriteItems.TryRemove(key, out obj);
        }

        public bool Remove(int key) {
            return this._backStore.Remove(key);
        }

        #endregion

        public void Run() {
            ConcurrentBag<Cacheable> items = new ConcurrentBag<Cacheable>();

            for(int i = 0; i < 1024*1024; i++) {
                using(IBinaryWriter writer = this._backStore.Write(i)) {
                    Cacheable item=new Cacheable(i, this._cache) { Data = this._rand.Next() };
                    //this._cache.Put(key, item);
                    items.Add(item);
                }
            }

            //Parallel.For(0, int.MaxValue, i => {
            //    if(items.Count < this._maxCount) {
            //        int key;
            //        using(IBinaryWriter writer = this._backStore.CreateWriter(out key)) {
            //            this._cache.Put(key, new Cacheable(key, this._cache) { Data = this._rand.Next() });
            //        }
            //        items.Add(this._cache[key]);
            //    } else {
            //        Cacheable item;
            //        items.TryTake(out item);
            //    }
            //    Console.WriteLine("{1}, Cached Items Count: {0}", this._cache.Count, i);
            //});

            while(this._cache.Count > 0) {
                Thread.Sleep(2000);
                Console.WriteLine(this._cache.Count);
            }

            System.Console.ReadLine();
        }
    }
}
